package com.ynet.middleground.mobile.approve.service;

import java.time.LocalDateTime;
import java.util.*;
import java.util.stream.Collectors;

import org.apache.commons.lang3.StringUtils;
import org.slf4j.helpers.MessageFormatter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.dingtalk.api.DefaultDingTalkClient;
import com.dingtalk.api.DingTalkClient;
import com.dingtalk.api.request.OapiMessageCorpconversationAsyncsendV2Request;
import com.dingtalk.api.response.OapiMessageCorpconversationAsyncsendV2Response;
import com.google.common.base.Throwables;
import com.ifp.util.core.DateUtil;
import com.taobao.api.ApiException;
import com.ynet.core.common.BaseReqObj;
import com.ynet.core.common.ServiceResult;
import com.ynet.core.exception.BusinessException;
import com.ynet.core.log.IfpLogger;
import com.ynet.core.util.IDUtil;
import com.ynet.middleground.approve.constant.OperatingTypeEnum;
import com.ynet.middleground.approve.constant.WorkflowConstants;
import com.ynet.middleground.approve.dto.HistoryTaskDTO;
import com.ynet.middleground.approve.dto.ProcessInstanceDTO;
import com.ynet.middleground.approve.dto.TaskDTO;
import com.ynet.middleground.mobile.approve.conf.AppConfig;
import com.ynet.middleground.mobile.approve.constant.MsgTypeEnum;
import com.ynet.middleground.mobile.approve.constant.SystemConstant;
import com.ynet.middleground.mobile.approve.constant.UrlConstant;
import com.ynet.middleground.mobile.approve.entity.TaskOperateStage;
import com.ynet.middleground.mobile.approve.request.SendLendersCaptchaCodeReq;
import com.ynet.middleground.mobile.approve.response.ApprovePersonDTO;
import com.ynet.middleground.mobile.approve.utils.ApiHelper;
import com.ynet.middleground.mobile.approve.utils.EnumUtil;
import com.ynet.middleground.user.dto.UserInfoDto;

/**
 * 项目名：ifp-mobile-approve <br>
 * 包名：com.ynet.middleground.mobile.approve.service <br>
 * 类名：MessageService <br>
 * 描述： 发送钉钉消息服务
 * 
 * @author Suremotoo
 * @create 2021-03-31 16:34
 */
@Service
public class MessageService {

    @Autowired
    ApiHelper apiHelper;

    @Autowired
    AppConfig appConfig;

    @Autowired
    ApproveCenterService approveCenterService;

    @Autowired
    UserCenterService userCenterService;

    @Autowired
    private TaskOperateStageService taskOperateStageService;

    @Lazy
    @Autowired
    private MessageService lazyMessageService;

    /**
     * 发送钉钉工作通知消息 <br>
     * 钉钉工作通知消息参考文档
     * {@link https://developers.dingtalk.com/document/app/asynchronous-sending-of-enterprise-session-messages}
     * 
     * @param dingtalkUserId 用户钉钉 id
     * @param fullContent 消息正文内容
     * @return 发送消息的任务 id
     */
    public ServiceResult<Long> sendWorkingMessage(String dingtalkUserId, String fullContent) {
        String accessToken = apiHelper.getAccessToken();
        ServiceResult<Long> serviceResult = new ServiceResult<>();
        DingTalkClient client = new DefaultDingTalkClient(UrlConstant.URL_CREATE_MESSAGE);
        OapiMessageCorpconversationAsyncsendV2Request request = new OapiMessageCorpconversationAsyncsendV2Request();
        request.setAgentId(appConfig.getAgentId());
        request.setUseridList(dingtalkUserId);
        request.setHttpMethod("POST");

        request.setMsg(fullContent);
        OapiMessageCorpconversationAsyncsendV2Response response;
        try {
            response = client.execute(request, accessToken);
            IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "向钉钉用户 {} 发送信息,request:{},response: {}", dingtalkUserId, fullContent, JSON.toJSONString(response));
        } catch (ApiException e) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "向钉钉用户发送信息异常, case: {}",
                Throwables.getStackTraceAsString(e));
            serviceResult.setError(e.getErrCode(), e.getErrMsg());
            return serviceResult;
        }
        return serviceResult.setResult(response.getTaskId());
    }

    /**
     * 根据类型获取发送的消息<br>
     * 钉钉工作通知消息格式参考文档
     * {@link https://developers.dingtalk.com/document/app/message-types-and-data-format?spm=ding_open_doc.document.0.0.23e73526oWoqnj#topic-1945727}
     *
     * 
     * @param type
     * @return
     */
    public String getMessageTest(String type, String content) {
        String msg = "";
        switch (type) {
            case "img":
                break;
            case "link":
                msg =
                    "{\"msgtype\":\"link\",\"link\":{\"messageUrl\":\"http://www.baidu.com\",\"picUrl\":\"@lALOACZwe2Rk\",\"title\":\"测试"
                        + DateUtil.getCurrentTime() + "\",\"text\":\"测试\"}}";
                break;
            case "markdown":
                msg = "{\"msgtype\":\"markdown\",\"markdown\":{\"title\":\"首屏会话透出的展示内容\",\"text\":\"# 这是支持markdown的文本"
                    + DateUtil.getCurrentTime()
                    + " \\n## 标题2  \\n* 列表1 \\n![alt 啊](https://cloudfront.modao.cc/uploads4/avatars/208/2088691/forum_132.jpeg)\"}}";
                break;
            case "oa":
                msg =
                    "{\"msgtype\":\"oa\",\"oa\":{\"message_url\":\"http://dingtalk.com\",\"head\":{\"bgcolor\":\"FFBBBBBB\",\"text\":\"头部标题\"},\"body\":{\"title\":\"正文标题"
                        + DateUtil.getCurrentTime()
                        + "\",\"form\":[{\"key\":\"姓名:\",\"value\":\"张三\"},{\"key\":\"年龄:\",\"value\":\"20\"},{\"key\":\"身高:\",\"value\":\"1.8米\"},{\"key\":\"体重:\",\"value\":\"130斤\"},{\"key\":\"学历:\",\"value\":\"本科\"},{\"key\":\"爱好:\",\"value\":\"打球、听音乐\"}],\"rich\":{\"num\":\"15.6\",\"unit\":\"元\"},\"content\":\"大段文本大段文本大段文本大段文本大段文本大段文本\",\"image\":\"@lADOADmaWMzazQKA\",\"file_count\":\"3\",\"author\":\"李四 \"}}}";
                break;
            case "card1":
                msg =
                    "{\"msgtype\":\"action_card\",\"action_card\":{\"title\":\"是透出到会话列表和通知的文案\",\"markdown\":\"支持markdown格式的正文内容 "
                        + DateUtil.getCurrentTime()
                        + "\",\"single_title\":\"查看详情\",\"single_url\":\"https://open.dingtalk.com\"}}";
                break;
            case "card2":
                msg =
                    "{\"msgtype\":\"action_card\",\"action_card\":{\"title\":\"是透出到会话列表和通知的文案\",\"markdown\":\"支持markdown格式的正文内容"
                        + DateUtil.getCurrentTime()
                        + "\",\"btn_orientation\":\"1\",\"btn_json_list\":[{\"title\":\"一个按钮\",\"action_url\":\"https://www.taobao.com\"},{\"title\":\"两个按钮\",\"action_url\":\"https://www.tmall.com\"}]}}";
                break;
            case "text":
                msg = "{\"msgtype\":\"text\",\"text\":{\"content\":\"" + content + DateUtil.getCurrentTime() + "。\"}}";
                break;
            default:
        }
        return msg;
    }

    /**
     * 封装钉钉消息模板内容
     * 
     * @param content 消息内容
     * @return 完整钉钉消息内容
     */
    public String setDingtalkTextMessage(String content) {
        return "{\"msgtype\":\"text\",\"text\":{\"content\":\"" + content + "\"}}";
    }

    /**
     * 获取消息模板
     * 
     * @param msgTypeEnum 推送消息类型 {@link MsgTypeEnum}
     * @return 消息模板
     */
    public String getMsgTemplate(MsgTypeEnum msgTypeEnum) {
        String notifyMsg;
        switch (msgTypeEnum) {
            case TODO:
                notifyMsg = "流程待办通知\n您于 {} 收到一条【{}（{}）】任务，请尽快前往处理。";
                break;
            case PASSED:
                notifyMsg = "流程通过通知\n您于 {} 审批的【{}（{}）】指令执行成功-审批通过，请知晓。";
                break;
            case NOT_PASSED:
                notifyMsg = "流程完结通知\n您于 {} 审批的【{}（{}）】指令执行失败-审批未通过，请重新处理。";
                break;
            case REJECT:
                notifyMsg = "流程驳回通知\n您于 {} 审批的【{}（{}）】被驳回，请知晓。";
                break;
            case RETURN:
                notifyMsg = "流程退回通知\n您于 {} 审批的【{}（{}）】被退回，请知晓。";
                break;
            case FINISHED:
                notifyMsg = "流程完结通知\n您于 {} 审批的【{}（{}）】已处理完结，请知晓。";
                break;
            default:
                notifyMsg = "";
                break;
        }
        return notifyMsg;
    }

    /**
     * 发生审批通知到钉钉
     * 
     * @param msgTypeEnum 推送消息类型 {@link MsgTypeEnum}
     * @param assignee 任务审核人
     * @param processInstanceId 流程实例 id
     * @param taskId 当前任务 id，该参数为有指令时必填
     */
    public void sendDingtalkMessage(MsgTypeEnum msgTypeEnum, Integer assignee, String processInstanceId,
        String taskId) {
        String msgTemplate = getMsgTemplate(msgTypeEnum);
        switch (msgTypeEnum) {
            // 指令通过、不通过
            case PASSED:
            case NOT_PASSED:
                sendDingtalkMessageForCommand(assignee, taskId, msgTemplate);
                break;
            // 待办通知
            case TODO:
                sendDingtalkMessageForTodo(processInstanceId, assignee, msgTemplate);
                break;
            // 驳回至发起人、退回、流程完结通知处理一样
            case REJECT:
            case RETURN:
            case FINISHED:
                sendDingtalkMessageForApprovedMessageExt(msgTypeEnum, msgTemplate, assignee, processInstanceId);
                break;
            default:
                break;
        }

    }

    /**
     * 发送带指令情况钉钉通知消息
     * 
     * @param assignee 任务处理人
     * @param taskId 任务 id
     * @param msgTemplate 推送消息模板
     */
    protected void sendDingtalkMessageForCommand(Integer assignee, String taskId, String msgTemplate) {
        TaskDTO task = approveCenterService.getTask(assignee, taskId);
        Map<String, Object> processVariables = task.getProcessVariables();
        // 获取流程名称
        Object processName = processVariables.get(WorkflowConstants.FLOW_PROCESS_NAME);
        // 组织消息内容
        String msgContent =
            MessageFormatter
                .arrayFormat(msgTemplate,
                    new String[] {DateUtil.format(task.getEndTime()),
                        processName == null ? "" : String.valueOf(processName), task.getProcessInstanceId()})
                .getMessage();
        String message = setDingtalkTextMessage(msgContent);
        UserInfoDto userInfo = userCenterService.getUserInfo(assignee, SystemConstant.CHANNEL, assignee, null);
        if (userInfo != null && userInfo.getUserBaseInformationDto() != null
            && StringUtils.isNotBlank(userInfo.getUserBaseInformationDto().getDingtalkUserid())) {
            sendWorkingMessage(userInfo.getUserBaseInformationDto().getDingtalkUserid(), message);
        }
    }

    /**
     * 发送待办任务钉钉通知消息
     * 
     * @param processInstanceId 流程实例 id
     * @param assignee 任务处理人
     * @param msgTemplate 推送消息模板
     */
    protected void sendDingtalkMessageForTodo(String processInstanceId, Integer assignee, String msgTemplate) {
        ServiceResult<List<TaskDTO>> activeTaskResult = approveCenterService.getActiveTask(processInstanceId, assignee);
        if (!activeTaskResult.isSuccess()) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "MQ 执行审批中心 查询流程当前活动服务异常，异常原因: {}:{}", activeTaskResult.getErrorCode(), activeTaskResult.getErrorMsg());
        }
        List<TaskDTO> result = activeTaskResult.getResult();
        if (result != null && !result.isEmpty()) {
            result.forEach(task -> {
                Map<String, Object> processVariables = task.getProcessVariables();
                Map<String, Object> taskLocalVariables = task.getVariables();
                // 获取当前任务是否可以在移动端可处理
                Object mobileTag = taskLocalVariables.get(WorkflowConstants.FLOW_TASK_MOBILE_PROCESS);
                if (mobileTag != null) {
                    // 获取流程名称
                    Object processName = processVariables.get(WorkflowConstants.FLOW_PROCESS_NAME);
                    // 组织消息内容
                    String fullMsg = MessageFormatter
                        .arrayFormat(msgTemplate,
                            new String[] {DateUtil.getCurrentTime(),
                                processName == null ? "" : String.valueOf(processName), task.getProcessInstanceId()})
                        .getMessage();
                    // 先判断任务是否是已有明确的人
                    if (StringUtils.isNotBlank(task.getAssignee())) {
                        UserInfoDto userInfo = userCenterService.getUserInfo(Integer.valueOf(task.getAssignee()),
                            SystemConstant.CHANNEL, assignee, null);
                        if (userInfo != null && userInfo.getUserBaseInformationDto() != null
                            && StringUtils.isNotBlank(userInfo.getUserBaseInformationDto().getDingtalkUserid())) {
                            IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
                                "获取当前活动任务要推送的待办人员，仅为：{}", task.getAssignee());
                            sendWorkingMessage(userInfo.getUserBaseInformationDto().getDingtalkUserid(),
                                setDingtalkTextMessage(fullMsg));
                            return;
                        } else {
                            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                                "任务：{} 的处理人： {} 暂未绑定钉钉号，无法发送待办消息", task.getId(), task.getAssignee());
                        }
                    } else {
                        // 若为多人或多角色的，查询出来符合的人员再一次发送通知
                        // 获取当前流程可处理渠道
                        String operateChannels =
                            (String)processVariables.get(WorkflowConstants.FLOW_CAN_OPERATE_CHANNEL_SIGNAL);
                        List<ApprovePersonDTO> toSendUsers = userCenterService.getMessagesApprover(operateChannels,
                            assignee, task.getAssignee(), task.getCandidateUsers(), task.getCandidateGroups());
                        IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(), "获取当前活动任务要推送的待办人员{}",
                            JSONArray.toJSONString(toSendUsers));
                        if (toSendUsers != null && !toSendUsers.isEmpty()) {
                            // 循环发送消息
                            toSendUsers.forEach(
                                user -> sendWorkingMessage(user.getOtherMessage(), setDingtalkTextMessage(fullMsg)));
                        }
                    }
                }
            });
        }

        // 再查询流程是否已结束，若已结束，再发生流程完结的通知
        BaseReqObj baseReqObj = new BaseReqObj();
        baseReqObj.setTraceId(IDUtil.getSecureRandomId());
        baseReqObj.setChannel(SystemConstant.CHANNEL);
        baseReqObj.setOperationUserId(assignee);
        baseReqObj.setRequestTime(new Date());
        ProcessInstanceDTO processInstanceDTO = approveCenterService.checkProcessEnd(processInstanceId, baseReqObj);
        if (processInstanceDTO == null) {
            sendDingtalkMessageForApprovedMessageExt(MsgTypeEnum.FINISHED, getMsgTemplate(MsgTypeEnum.FINISHED),
                assignee, processInstanceId);
        }

    }

    /**
     * 发送 退回、驳回、流程完结 3 种通知消息
     * 
     * @param msgTypeEnum 消息类型
     * @param msgTemplate 推送消息模板
     * @param userId 审批人
     * @param processInstanceId 流程实例 id
     */
    protected void sendDingtalkMessageForApprovedMessageExt(MsgTypeEnum msgTypeEnum, String msgTemplate, Integer userId,
        String processInstanceId) {
        // 根据流程实例编号查询流程处理记录
        ServiceResult<List<HistoryTaskDTO>> serviceResult =
            approveCenterService.listHistoryTaskByProcessInstanceId(userId, processInstanceId);
        if (!serviceResult.isSuccess()) {
            IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(), "查询流程处理记录服务异常, 错误信息: {}-{}",
                serviceResult.getErrorCode(), serviceResult.getErrorMsg());
            return;
        }
        List<HistoryTaskDTO> taskList = serviceResult.getResult();
        IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
            "发送退回、驳回、流程完结消息前，查询流程处理记录服务结果 taskList: {}", JSON.toJSONString(taskList));
        // 排除发起人，故处理记录肯定大于 1
        if (taskList == null || taskList.size() <= 1) {
            return;
        }
        switch (msgTypeEnum) {
            // 驳回、退回 时，当前驳回或退回的审批人无需接受通知消息， 所以从结果集合中移除当前驳回或退回的任务信息
            case REJECT:
            case RETURN:
                // 因为查询流程处理记录结果最后一条是当前流程活动的任务, 所以移除最后 2 个元素: 当前驳回的任务、当前活动任务
                taskList.remove(taskList.size() - 1);
                taskList.remove(taskList.size() - 1);
                IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
                    "发送退回、驳回、流程完结消息前，查询流程处理记录服务结果 taskList 退回、驳回移除最后一个: {}", JSON.toJSONString(taskList));
                break;
            default:
                break;
        }

        // 过滤处理审批记录，找出需要推送消息的审批任务信息
        ArrayList<HistoryTaskDTO> resultTasks = taskList.stream().parallel().filter(task -> {
            Map<String, Object> taskLocalVariables = task.getTaskLocalVariables();
            // 获取任务是否有审批结果记录
            Object userApproveResult = taskLocalVariables.get(WorkflowConstants.USER_TASK_APPROVE_RESULT);
            // 获取当前任务是否可以在移动端可处理
            Object mobileTag = taskLocalVariables.get(WorkflowConstants.FLOW_TASK_MOBILE_PROCESS);
            // 过滤取出仅移动端可处理的任务，且任务已被审批
            return mobileTag != null && StringUtils.isNotBlank(task.getAssignee()) && task.getEndTime() != null
                && userApproveResult != null;
        }).collect(Collectors.collectingAndThen(
            // 根据任务处理人 + 任务 key 去重复
            Collectors.toCollection(
                () -> new TreeSet<>(Comparator.comparing(task -> task.getAssignee() + task.getTaskDefinitionKey()))),
            ArrayList::new));

        IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(), "发送退回、驳回、流程完结消息前，处理任务列表： {}",
            JSON.toJSONString(resultTasks));

        // 待推送消息的人员列表
        Map<String, String> dingtalkUserIds = new HashMap<>(8);
        Map<String, Boolean> dingtalkUserCache = new HashMap<>(8);
        if (resultTasks != null && !resultTasks.isEmpty()) {
            for (HistoryTaskDTO task : resultTasks) {
                Boolean hasDingtalk = dingtalkUserCache.get(task.getAssignee());
                String dingtalkId = dingtalkUserIds.get(task.getAssignee());
                if (hasDingtalk == null && dingtalkId == null) {
                    UserInfoDto userInfo = userCenterService.getUserInfo(Integer.valueOf(task.getAssignee()),
                        SystemConstant.CHANNEL, userId, null);
                    dingtalkUserCache.put(task.getAssignee(), false);
                    if (userInfo != null && userInfo.getUserBaseInformationDto() != null
                        && StringUtils.isNotBlank(userInfo.getUserBaseInformationDto().getDingtalkUserid())) {
                        dingtalkUserIds.put(task.getAssignee(),
                            userInfo.getUserBaseInformationDto().getDingtalkUserid());
                        dingtalkUserCache.put(task.getAssignee(), true);
                    }
                }

                Boolean hasDingtalkSecond = dingtalkUserCache.get(task.getAssignee());
                String dingtalkIdSecond = dingtalkUserIds.get(task.getAssignee());

                if (hasDingtalkSecond && StringUtils.isNotBlank(dingtalkIdSecond)) {
                    Map<String, Object> processVariables = task.getProcessVariables();
                    Object processName = processVariables.get(WorkflowConstants.FLOW_PROCESS_NAME);
                    String fullMsg = MessageFormatter
                        .arrayFormat(msgTemplate,
                            new String[] {DateUtil.format(task.getEndTime()),
                                processName == null ? "" : String.valueOf(processName), task.getProcessInstanceId()})
                        .getMessage();
                    // 发送消息
                    sendWorkingMessage(dingtalkIdSecond, setDingtalkTextMessage(fullMsg));
                }
            }
        }
    }

    /**
     * 推送审批流程处理后的消息，同时发送钉钉通知
     * 
     * <pre>
     * 保留备用
     * </pre>
     * 
     * @param serviceResult 调用审批中心服务后的结果
     * @param taskOperateStage 任务操作记录对象（传入该对象前，需要将更新的属性都设置进去）
     * @param msgTypeEnum 消息类型
     * @param operatingTypeEnum 审批操作类型
     * @return errorMsg 错误提示语
     */
    @Transactional(rollbackFor = Exception.class)
    public String pushApproveAfterMsgQueueAndSendDingTalkMsg(ServiceResult serviceResult,
        TaskOperateStage taskOperateStage, MsgTypeEnum msgTypeEnum, OperatingTypeEnum operatingTypeEnum) {
        String errorMsg = null;
        if (serviceResult != null) {
            if (serviceResult.isSuccess()) {
                taskOperateStage.setGmtModified(LocalDateTime.now());
                taskOperateStageService.updateById(taskOperateStage);
                // 只有未签章情况下才算执行完成，再推送执行后消息及发送钉钉通知
                if (Boolean.FALSE.equals(taskOperateStage.getIsToBeSigned())) {
                    // 给渠道推送审批操作执行后的消息
                    approveCenterService.pushApproveAfterMsgQueue(taskOperateStage);
                    // 推送消息
                    try {
                        sendDingtalkMessage(msgTypeEnum, taskOperateStage.getAssignee(),
                            taskOperateStage.getProcessInstanceId(), null);

                    } catch (Exception e) {
                        errorMsg = MessageFormatter.arrayFormat("MQ 推送 {} 通知服务异常，异常原因: {}",
                            new String[] {operatingTypeEnum.getDesc(), e.getMessage()}).getMessage();
                    }
                }
            } else {
                IfpLogger.error(Thread.currentThread().getStackTrace()[1].getMethodName(),
                    "MQ 执行审批中心 {} 服务异常，异常原因: {}:{}", operatingTypeEnum.getDesc(), serviceResult.getErrorCode(),
                    serviceResult.getErrorMsg());
                errorMsg = MessageFormatter.arrayFormat("MQ 执行审批中心 {} 服务异常，异常原因: {}:{}", new String[] {
                    operatingTypeEnum.getDesc(), serviceResult.getErrorCode(), serviceResult.getErrorMsg()})
                    .getMessage();
            }
        }
        return errorMsg;
    }

    /**
     * 审批完成后推送钉钉通知消息
     * 
     * @param req 基本请求信息
     */
    public void sendDingtalkNotificationAfterApproval(SendLendersCaptchaCodeReq req) {
        TaskOperateStage taskOperateStage = taskOperateStageService.getTaskOperateStageByTaskId(req.getTaskId());
        // 如果为空，说明当前任务不是从移动审批处理的。
        if (taskOperateStage == null) {
            sendDingtalkMessage(MsgTypeEnum.TODO, req.getUserId(), req.getProcessInstanceId(), null);
            IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
                "任务 {} 审批完成后（非移动审批处理）推送钉钉通知消息, req：{}", req.getTaskId(), req);
            return;
        }
        IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(),
            "任务 {} 审批完成后推送钉钉通知消息, taskOperateStage：{}", req.getTaskId(), taskOperateStage);
        OperatingTypeEnum operatingTypeEnum = EnumUtil.getEnumObject(OperatingTypeEnum.class,
            e -> e.getCode() == Integer.parseInt(taskOperateStage.getOperateType()), "操作类型参数值错误", "ECMA0001");
        MsgTypeEnum msgTypeEnum = null;
        switch (operatingTypeEnum) {
            // 同意
            case PASS:
                msgTypeEnum = MsgTypeEnum.TODO;
                break;
            // 退回至发起人
            case RETURN:
                msgTypeEnum = MsgTypeEnum.RETURN;
                break;
            // 驳回至发起人
            case REJECT_TO_STARTER_DISAGREE:
            case REJECT_TO_STARTER_MODIFICATION:
                msgTypeEnum = MsgTypeEnum.REJECT;
                break;
            default:
                break;
        }
        String errorMsg =
            lazyMessageService.sendDingtalkNotificationAfterApproval(taskOperateStage, msgTypeEnum, operatingTypeEnum);
        if (StringUtils.isNotEmpty(errorMsg)) {
            throw new BusinessException("ECUC0030", errorMsg);
        }
    }

    /**
     * 审批完成后推送钉钉通知消息
     * 
     * @param taskOperateStage 任务操作记录对象（传入该对象前，需要将更新的属性都设置进去）
     * @param msgTypeEnum 消息类型
     * @param operatingTypeEnum 审批操作类型
     * @return errorMsg 错误提示语
     */
    @Transactional(rollbackFor = Exception.class)
    public String sendDingtalkNotificationAfterApproval(TaskOperateStage taskOperateStage, MsgTypeEnum msgTypeEnum,
        OperatingTypeEnum operatingTypeEnum) {
        String errorMsg = null;

        // 只有未签章情况下才算执行完成，再推送执行后消息
        if (Boolean.FALSE.equals(taskOperateStage.getIsToBeSigned())) {
            // 推送消息
            try {
                sendDingtalkMessage(msgTypeEnum, taskOperateStage.getAssignee(),
                    taskOperateStage.getProcessInstanceId(), null);
            } catch (Exception e) {
                errorMsg = MessageFormatter
                    .arrayFormat("MQ 推送 {} 通知服务异常，异常原因: {}", new String[] {operatingTypeEnum.getDesc(), e.getMessage()})
                    .getMessage();
            }
        }

        // 判断是否有指令，再推送指令消息
        if (Boolean.TRUE.equals(taskOperateStage.getHasCommand())) {
            Integer assignee = taskOperateStage.getAssignee();
            IfpLogger.info(Thread.currentThread().getStackTrace()[1].getMethodName(), "任务 {} 给 {} 推送指令消息：{}",
                taskOperateStage.getTaskId(), assignee, taskOperateStage.getReason());
            MsgTypeEnum commandMsgTypeEnum =
                (taskOperateStage.getCommandResult() != null && taskOperateStage.getCommandResult())
                    ? MsgTypeEnum.PASSED : MsgTypeEnum.NOT_PASSED;
            sendDingtalkMessage(commandMsgTypeEnum, assignee, taskOperateStage.getProcessInstanceId(),
                taskOperateStage.getTaskId());
        }
        return errorMsg;
    }

}
